package cloud.dwz.tool;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.Serializable;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * jwt(JSON Web Token)令牌工具类,公钥加密(私钥解密)
 * @作者 田应平
 * @版本 v1.0
 * @创建时间 2020-02-12 23:53
 * @QQ号码 444141300
 * @Email service@yinlz.com
 * @官网 <url>http://www.yinlz.com</url>
*/
public final class ToolJWT implements Serializable{

    //如设置Token过期时间15分钟，建议更换时间设置为Token前5分钟,通过try catch 获取过期
    private final static long access_token = 1000 * 60 * 45;//当 refreshToken 已过期了，再判断 accessToken 是否已过期,

    /**一般更换新的accessToken小于5分钟则提示需要更换新的accessToken*/
    private final static long refresh_token = 1000 * 60 * 40;//仅做token的是否需要更换新的accessToken标识,小于5分钟则提示需要更换新的accessToken

    private final static String issuer = "www.fwtai.com";//jwt签发者

    /**2048的密钥位的公钥*/
    private final static String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsichpewxmW0e7CD+VeXf4hl5Lc7xmRerAwLvP4hMiC5Mx+VwZJiHhqfS7kwNopHmspSKg0hT3njm/VEg059F8gFkEYCAiEzJZniR58U2UzGjPImdMoLsAlnyyAzWcX/RK0JYnV71g8bvILJQVQxGI9jzmBOsLPcghUwShtvSvBmfxtcgTxsm+9GqASFjUIb/UyR3CJQyLbTFpWQ6lK/X+8VzFycvT1DZi9uj5dx4DJ3BOnmkX2guuHUsyhg6XXHYMfTJloZ9eVd2yrLPIXhcGH+JqvPWBgzZRauydDIG3T1aJEbig1TKzoIJ2p3AWdQVERshNGZjM3CpyWNfQzhXgQIDAQAB";
    /**2048的密钥位的私钥*/
    private final static String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCyJyGl7DGZbR7sIP5V5d/iGXktzvGZF6sDAu8/iEyILkzH5XBkmIeGp9LuTA2ikeaylIqDSFPeeOb9USDTn0XyAWQRgICITMlmeJHnxTZTMaM8iZ0yguwCWfLIDNZxf9ErQlidXvWDxu8gslBVDEYj2POYE6ws9yCFTBKG29K8GZ/G1yBPGyb70aoBIWNQhv9TJHcIlDIttMWlZDqUr9f7xXMXJy9PUNmL26Pl3HgMncE6eaRfaC64dSzKGDpdcdgx9MmWhn15V3bKss8heFwYf4mq89YGDNlFq7J0MgbdPVokRuKDVMrOggnancBZ1BURGyE0ZmMzcKnJY19DOFeBAgMBAAECggEATLN8USt3z06j2ZX66pCl63p8KbTHX9pIzmsdBoVwLdGBTS8DTMw+zULEFcS4uh2G7av2Vr91HHuBN98N+w6/1omyw7ZHoGcu7udKKjdGRFtCpHW7MFwdsIV5eZfNW+Bj9fQCov6XRb0Ih1M+Mt+t8xEdD4SoxFNBtSIjd9x8xm6ngB1o14ByuZOf0NKkjYaoGZspL6ZWGGnYUIkvI8ZafA2f3V4x2gEZhDudCINIsxcUCH6NtStyoKNqXs1WIvfsIROJLCiH/pq+dVEox8vVN03gFsZjYJrUpHPh1rjqBui3GQTdmIOz93cmglHWTbnHyuHBvrUr9xO2XfEbT+0+4QKBgQD7QUP4EmwTEw2De5G5fuo5b3Zb3DPasNf6P2WQPzVpKYujB6BKDLLaoC+Qf2d3zAn05pZgsnLCHsUCsr3EaF+IQnRWyElAeNL71zRYmzIyHD2+A9Shm3O82YJ1AX4Bgf3XIqapZ1/eya6K0w7Cch370pPNq9/Y6s8ExABLUQRBdwKBgQC1hHEI1i9iWl+cSJG4ME3TBkXHdbB+Z05OwH36kM0e4rN/ryeHVgrpK3+kvxPyVMHlcfx3ieQBk3a5WY05WQksy93XoEdcuoXQPExTUChuntCSV7rgKCDdt6SAWIxiQ6I+HAKV5NrAXiXARKuVdlYFdZrhuBPDJnKiUgoUcd8sxwKBgHzqkudYCWFmXJadLfNCdSL/FqJPFXzJ5Wcmc9FGi8BwBluUpA2MyLNHZr55iTCXQe//YI5Myh9W+u3/fbeTiMOO/pZS15B1Qbgx43WWG1usjMnWz7VVOzKOFX0ks8f+H/0A3klk+tYiAWRF0JW99MhJhnB/3jfTbf9Zjyfi7LG7AoGARmLeBgQwCZ5rtsl5IAgzFWI4Srj7QHjEp8sCzte5goF8A0YjONqHjpUcgQDLNkvhiBxkPuJMdMP+5K6yEYvR//npw0RWh8GYRAAtxJyvoP2aeTTltSg6Xl9ovZAYl74MOrNX2VIN9UxMrTqzolhp0OIiteZQW3scNo9Dc1wYo18CgYEA0SzHK5s0lHPWAvJBIp0/HaOCZ3Sj1RrGYRjwhefOJpS2rrYB8gn8NamPCGP3kV1s0KPlteDQrLhaMcRPGmX0bqJ34Qcynt00FLuXigE/ur5pe+RVefnhy1gSUki2E/a55o+FGG5jujHVw8h5EPW/bNgXLe3WGuzrM+3f1ThKG5U=";

    /**java生成的私钥是pkcs8格式的公钥是x.509格式*/
    private static PublicKey getPublicKey(){
        try {
            final byte[] keyBytes = Base64.getDecoder().decode(publicKey);
            final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static PrivateKey getPrivateKey(){
        try {
            final byte[] keyBytes = Base64.getDecoder().decode(privateKey);
            final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // setSubject 不能和s etClaims() 同时使用,如果用不到 userId() 的话可以把setId的值设为 userName !!!
    private static String createToken(final Long userId,final Object value,final long expiryDate){
        final ExecutorService threadPool = Executors.newCachedThreadPool();
        final Future<String> future = threadPool.submit(new Callable<String>(){
            @Override
            public String call() throws Exception{
                final long date = System.currentTimeMillis();
                final JwtBuilder builder = Jwts.builder().signWith(getPrivateKey(),SignatureAlgorithm.RS384);
                if(value != null){
                    builder.claim(String.valueOf(userId),value);
                }
                return builder.setId(String.valueOf(userId)).setIssuer(issuer).setIssuedAt(new Date(date)).setExpiration(new Date(date + expiryDate)).compact();
            }
        });
        try {
            return future.get();
        } catch (final Exception e) {
            return null;
        }finally{
            threadPool.shutdown();
        }
    }

    private static Claims parser(final String token){
        return Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(token).getBody();
    }

    private static Object extract(final String token,final String key){
        return parser(token).get(key);
    }

    /**其实id就是本项目的userId*/
    public static String getId(final String token){
        return parser(token).getId();
    }

    /**从微信公众号的token获取userId账号id及userType账号类型,1是系统角色;2是员工角色*/
    public static HashMap<String,Object> getUserInfo(final String token){
        HashMap<String,Object> map = new HashMap<>();
        final String id = getId(token);
        map.put("userId",Long.parseLong(id));
        map.put("userType",extract(token,id));
        return map;
    }

    /**
     * 验证token是否已失效,返回true已失效,否则有效
     * @param token
     * @作者 田应平
     * @QQ 444141300
     * @创建时间 2020年2月24日 16:19:00
    */
    public static boolean tokenExpired(final String token) {
        try {
            return parser(token).getExpiration().before(new Date());
        } catch (final ExpiredJwtException exp) {
            return true;
        }
    }

    /**仅作为是否需要刷新的accessToken标识,不做任何业务处理*/
    public static String expireRefreshToken(final Long userId){
        return createToken(userId,null,refresh_token);
    }

    /**生成带认证实体且有权限的token,最后个参数是含List<String>的角色信息,*/
    public static String expireAccessToken(final Long userId,final Object value){
        return createToken(userId,value,access_token);
    }
}